<?php

namespace app\models;

use app\utils\BaseUtil;
use app\utils\BaseUtils;
use Yii;
use app\components\BaseActiveRecord;
use yii\log\Logger;

/**
 * This is the model class for table "{{%sms}}".
 *
 * @property integer $id
 * @property integer $code
 * @property string $content
 * @property string $phone
 * @property integer $ip
 * @property integer $ctime
 * @property integer $status
 */
class Sms extends BaseActiveRecord {

    const SAFE_PHONE = 13812345678;
    const VERIFY_NUM = 5;
    const MAX_IP_NUM = 10;
    const expire_time = 600;

    protected $key = '9e45a1f68df3bdc9ce22f9ab739c7d75';
    protected $api = 'http://v.juhe.cn/sms/send';


    /**
     * @inheritdoc
     */
    public static function tableName() {
        return '{{%sms}}';
    }

    /**
     * @inheritdoc
     */
    public function rules() {
        return [
            [['code', 'ip', 'ctime', 'status'], 'integer'],
            [['content'], 'string', 'max' => 100],
            [['phone'], 'string', 'max' => 11]
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels() {
        return [
            'id' => Yii::t('app', 'ID'),
            'code' => Yii::t('app', 'Code'),
            'content' => Yii::t('app', 'Content'),
            'phone' => Yii::t('app', 'Phone'),
            'ip' => Yii::t('app', 'Ip'),
            'ctime' => Yii::t('app', 'Ctime'),
            'status' => Yii::t('app', 'Status'),
        ];
    }

    protected function validateSendLimit($phone) {
        $ip = BaseUtils::getClientIp();
        $zero_time = strtotime(date("Y-m-d"));

        $count = static::find()->where(['ip' => $ip])->andWhere(['>', 'ctime', $zero_time])->count();
        if ($count > self::MAX_IP_NUM) {
            return static::retErr('收不到验证短信？换个网络环境试试哦~如果你是土豪，那就关闭wifi，开启流量再注册吧！');
        }

        $models = static::find()->where(['phone' => $phone])->andWhere(['>', 'ctime', $zero_time])->orderBy('ctime desc')->asArray()->all();
        if (!empty($models)) {
            if (count($models) > self::VERIFY_NUM) {
                return static::retErr('一个手机号一天最多能发送' . self::VERIFY_NUM . '次验证码');
            }

            $msg_send_ret = current($models);
            if ($msg_send_ret['ctime'] + 60 > time()) {
                return static::retErr('操作太频繁，一分钟内只能发送一次验证码');
            }
        }
        return static::retOK();
    }

    public function send($phone, $tpl = '32853', $status = self::STATUS_NO) {
        if (!BaseUtils::isValid($phone)) {
            return static::retErr("手机号码{$phone}不合法");
        }

        if (YII_ENV_PROD) {
            $res = $this->validateSendLimit($phone);
            if ($res['code'] != 0) {
                return $res;
            }
        }

        $verify_code = $this->genCode(5);
        $minute = self::expire_time / 60;

        $res = $this->saveResult($phone, $tpl, $verify_code, $status);
        if (!YII_ENV_PROD) {
            return $res;
        }

        $params = [
            ['name' => 'code', 'value' => $verify_code],
            ['name' => 'minute', 'value' => $minute],
        ];
        return $this->sendSMS($phone, $params, $tpl);
    }

    public function verify($phone, $verify_code, $status = TRUE) {
        $key = "{$phone}_verify_fail_num";
        $verify_fail_num = Yii::$app->cache->get($key);
        if ($verify_fail_num >= self::VERIFY_NUM) {
            return self::retErr('验证码错误次数过多,请30分钟后在试');
        }

        if (!BaseUtils::isValid($phone)) {
            return static::retErr('手机号码不合法');
        }
        if (!preg_match('/^\d+$/', $verify_code)) {
            return static::retErr('验证码由纯数字字符（0到9）组成');
        }

        if ($phone == self::SAFE_PHONE && $verify_code == 1234) {
            return static::retOK();
        }

        /* @var $model Sms */
        $model = static::find()
            ->where(['phone' => $phone, 'code' => $verify_code, 'status' => static::STATUS_NO])
            ->orderBy('id desc')->one();
        if (!$model || $model->ctime + static::expire_time < time()) {
            $verify_fail_num = $verify_fail_num ? ($verify_fail_num + 1) : 1;
            Yii::$app->cache->set($key, $verify_fail_num, 3600);
            return self::retErr('验证码不正确或者已过期');
        }

        Yii::$app->cache->delete($key);

        if (TRUE === $status) {
            $model->status = static::STATUS_YES;
            return $model->save();
        }
        return static::retOK();
    }

    public function saveResult($phone, $content, $verify_code, $status = self::STATUS_NO) {
        $model = new Sms();
        $model->phone = $phone;
        $model->content = $content;
        $model->code = $verify_code;
        $model->ip = BaseUtils::getClientIp();
        $model->ctime = time();
        $model->status = $status;
        $res = $model->save();
        if ($res['code'] != 0) {
            return $res;
        }
        if (!YII_ENV_PROD) {
            $res['data']['code'] = $model->code;
        }
        return $res;
    }

    public function sendSMS($phone, $params, $tpl) {
        //#code#={verify_code}&#minute#=minute
        $values = [];
        foreach ($params as $item) {
            $values[] = "#{$item['name']}#={$item['value']}";
        }
        $tpl_value = join('&', $values);
        $data = [
            'tpl_id' => $tpl,
            'tpl_value' => urlencode($tpl_value),
            'key' => $this->key,
            'mobile' => $phone
        ];

        $url = $this->api . '?' . http_build_query($data);
        $response = $this->request($url);
        $res = json_decode($response, TRUE);
        if ($res['error_code'] > 0) {
            Yii::error($response, 'sms');
            return static::retErr($response);
        }
        return static::retOK($values);
    }

    public function genCode($len = 5) {
        $str = '';
        for ($i = 0; $i < $len; $i++) {
            $str .= mt_rand(1, 9);
        }
        return $str;
    }

    protected function request($url) {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }
}
